package com.tul.exercise.base.java8.stream.filter;


import com.tul.cusa.common.log.LogTracerFactory;
import com.tul.cusa.common.log.intf.ILogTracer;
import com.tul.cusa.common.util.MathUtil;
import com.tul.cusa.common.util.RandomUtil;
import com.tul.exercise.base.java8.stream.base.Dish;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import java.io.IOException;
import java.nio.charset.Charset;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import java.util.stream.Stream;

/**
 * @ClassName StremFilterTest
 * @Description stream 的筛选和切片
 * @Author tul 2018/12/22
 * @Version 1.0
 **/
public class StremFilterTest {

    /**
     * 日志
     */
    public static final ILogTracer TRACER = LogTracerFactory.getTracer(StremFilterTest.class);

    @Test
    @DisplayName("筛选出各异的元素")
    public void test01() {
        List<Integer> numbers = createRandomNumber(50);
        numbers.stream().filter(i -> i > 25)
                // 使用 distinct
                .distinct()
                .forEach(System.out::println);

    }

    @Test
    @DisplayName("截断流")
    public void test02() {
        List<Integer> numbers = createRandomNumber(50);
        numbers.stream().filter(i -> i > 25)
                // 使用 limit
                .limit(5)
                .forEach(System.out::println);

    }

    @Test
    @DisplayName("跳过元素")
    public void test03() {
        List<Integer> numbers = createRandomNumber(50);
        numbers.stream().filter(i -> i > 25)
                // 使用 skip
                .skip(5)
                .forEach(System.out::println);

    }

    @Test
    @DisplayName("映射:找出菜的名称及其名称长度")
    public void test04() {
        List<Dish> dishes = createDishes(25);
        List<String> names = dishes.stream().map(Dish::getName).collect(Collectors.toList());
        System.out.println(names);

        List<Integer> len = dishes.stream().map(Dish::getName).map(String::length).collect(Collectors.toList());
        System.out.println(len);

    }

    @Test
    @DisplayName("流的扁平化")
    public void test05() {
        List<String> worlds = Stream.of("Hello", "World").collect(Collectors.toList());
        // 错误的写法
        List list1 = worlds.stream().map(word -> word.split(""))
                .distinct()
                .collect(Collectors.toList());
        System.out.println(list1);

        // 正确的写法
        List list = worlds.stream().map(word -> word.split(""))
                .flatMap(Arrays::stream)
                .distinct()
                .collect(Collectors.toList());
        System.out.println(list);

    }

    @Test
    @DisplayName("流的扁平化02")
    public void test06() {

        List<Integer> n1 = Stream.of(1, 2, 3).collect(Collectors.toList());
        List<Integer> n2 = Stream.of(3, 4).collect(Collectors.toList());

        // 这里使用flatMap使每个流元素成为一个流
        List<int[]> pairs = n1.stream().flatMap(i -> {
            //System.out.println("i=" + i);
            return n2.stream().map(j -> {
                //System.out.println("j=" + j);
                return new int[]{i, j};
            });
        }).collect(Collectors.toList());

        pairs.stream().forEach(i -> {
            System.out.println("(" + i[0] + "," + i[1] + ")");
        });

    }

    @Test
    @DisplayName("查找与匹配")
    public void test07() {

        List<Dish> menu = createDishes(10);
        System.out.println(menu);

        // 判断是否有一个为vegetarian
        if (menu.stream().anyMatch(Dish::isVegetarian)) {
            System.out.println("menu has vegetarian");
        }

        // 判断所有的元素是否满足条件
        if (menu.stream().allMatch(d -> d.getCalories() < 200)) {
            System.out.println("all dish is low calory");
        }

        // 判断所有的元素都不满足条件
        if (menu.stream().noneMatch(d -> d.getCalories() > 25)) {
            System.out.println("all dish's calory is over 25");
        }

        // 查找
        Optional<Dish> dish = menu.stream().filter(d -> d.isVegetarian())
                .findAny();
        if (dish.isPresent()) {
            System.out.println(dish.get().getName() + " is" + "vegetable");
        }

    }

    @Test
    @DisplayName("归约求和")
    public void test08() {

        List<Integer> numbers = createRandomNumber(250);
        // 求和
        int sum = numbers.stream().reduce(0, (a, b) -> a + b);
        System.out.println("sum=" + sum);

        // 求最大值
        Optional<Integer> maxValue = numbers.stream().reduce(Integer::max);
        if (maxValue.isPresent()) {
            System.out.println("maxValue=" + maxValue.get());
        }

        // 求最小值
        Optional<Integer> minValue = numbers.stream().reduce(Integer::min);
        if (maxValue.isPresent()) {
            System.out.println("minValue=" + minValue.get());
        }

        List<Dish> menu = createDishes(10);
        int sumCalory = menu.stream().map(d -> d.getCalories())
                .collect(Collectors.toList())
                .stream().reduce(0, (a, b) -> a + b);
        System.out.println("tocalCalory=" + sumCalory);
    }

    @Test
    @DisplayName("数字流")
    public void test09() {
        List<Dish> menu = createDishes(10);

        // 转为为数字流，使用reduce求和
        int sum1 = menu.stream()
                // 转换成数字流
                .mapToInt(Dish::getCalories)
                .reduce(0, (a, b) -> a + b);
        System.out.println("sum=" + sum1);

        // 转为为数字流，使用sum求和
        int sum2 = menu.stream()
                .mapToInt(Dish::getCalories)
                .sum();
        System.out.println("sum=" + sum2);

        // 数字流转换为一般流
        Stream<Integer> stream = menu.stream()
                // 转换为数字流
                .mapToInt(Dish::getCalories)
                // 数字流转换为一般流
                .boxed();

        // OptionalInt默认值
        OptionalInt maxValue = menu.stream().mapToInt(Dish::getCalories).max();
        // 如果没有最大值，就设置一个默认值
        int max = maxValue.orElse(1);
        System.out.println("max=" + max);
    }

    @Test
    @DisplayName("数字范围")
    public void test10() {
        IntStream numbers = IntStream.rangeClosed(1, 100).filter(i -> i % 3 == 0);
        numbers.forEach(System.out::println);

        //System.out.println(numbers);
    }

    @Test
    @DisplayName("产生勾股数")
    public void test11() {
        int len = 100;
        Stream<double[]> numbers = IntStream.rangeClosed(1, len)
                // 这里需要将数字流转为一般流
                .boxed()
                .flatMap(a -> IntStream.rangeClosed(a, len)
                        .mapToObj(b -> new double[]{a, b, Math.sqrt(a * a + b * b)}))
                .filter(t -> t[2] % 1 == 0);

        numbers.forEach(i -> {
            System.out.println("(" + (int) i[0] + ", " + (int) i[1] + ", " + (int) i[2] + ")");
        });

    }

    @Test
    @DisplayName("flatMap与map区别")
    public void test12() {
        /**获取单词，并且去重**/
        List<String> list = Arrays.asList("hello welcome",
                "world hello", "hello world",
                "hello world welcome");

        //map和flatmap的区别
        list.stream().map(item -> Arrays.stream(item.split(" ")))
                .distinct().collect(Collectors.toList()).forEach(System.out::println);
        System.out.println("---------- ");

        // flatMap把每个数组元素都展开为流元素
        list.stream().flatMap(item -> Arrays.stream(item.split(" ")))
                .distinct().collect(Collectors.toList()).forEach(System.out::println);
    }


    @Test
    @DisplayName("创建流")
    public void test13() {

        // 1 由值创建流
        Stream<String> stringStream = Stream.of("tuliang", "zhangsan", "lisi");
        stringStream.forEach(System.out::println);

        // 2 由数组创建
        String[] strArray = {"tuliang", "zhangsan", "lisi"};
        Stream<String> stringStream2 = Arrays.stream(strArray);
        stringStream2.sorted().forEach(System.out::println);

        // 3 文件读取，见test14()

    }

    @Test
    @DisplayName("创建流:文件创建流")
    public void test14() {
        List<Dish> dises = createDishes(20);
        List<String> contents = new ArrayList<>();
        dises.stream().forEach(dish -> contents.add(dish.toString()));

        // 文件名称
        String fileName = "tmp.txt";

        // 写入文件
        try {
            Files.write(Paths.get(fileName), contents, Charset.forName("gbk"));
        } catch (IOException e) {
            TRACER.error(e);
        }

        // 读取文件
        try (Stream<String> fileStream = Files.lines(Paths.get(fileName), Charset.forName("gbk"))) {
            fileStream.forEach(line -> System.out.println(line));
        } catch (IOException e) {
            TRACER.error("文件读取失败:" + e.getMessage() + e);
        }
    }

    @Test
    @DisplayName("生成无限流")
    public void test15() {

        // 产生斐波拉契数列
        int[] initValue = new int[]{0, 1};
        Stream<int[]> stream = Stream.iterate(initValue, t -> new int[]{t[1], t[0] + t[1]})
                .limit(25);
        List<Integer> list = stream.map(t -> t[0])
                .collect(Collectors.toList());
        System.out.println(list);


        // 产生随机数序列
        Stream<Double> stream1 = Stream.generate(Math::random).limit(19);
        stream1.forEach(System.out::println);

    }

    @Test
    @DisplayName("使用collection计算最大值、最小值、平均值")
    public void test16() {

        List<Dish> dishes = createDishes(25);

        // Collectors.maxBy 计算某个属性的最大值的属性
        Comparator<Dish> comparator = Comparator.comparingInt(Dish::getCalories);
        Optional<Dish> max = dishes.stream().collect(Collectors.maxBy(comparator));
        if (max.isPresent()) {
            System.out.println(max.get());
        }

        // 计算某个属性的求和值
        int sum = dishes.stream().collect(Collectors.summingInt(Dish::getCalories));
        System.out.println("sum=" + sum);

        // 计算均值
        double average = dishes.stream().collect(Collectors.averagingInt(Dish::getCalories));
        System.out.println("average=" + average);

        // 计算统计情况：计数，求和，最大，最小，均值
        IntSummaryStatistics statistics = dishes.stream().collect(Collectors.summarizingInt(Dish::getCalories));
        System.out.println(statistics);

        // 连接字符串
        String str = dishes.stream().map(Dish::getName).collect(Collectors.joining());
        System.out.println(str);


    }

    /**
     * 创建多个随机数
     *
     * @param num 个数
     * @return List<Integer>
     */
    List<Integer> createRandomNumber(int num) {
        List<Integer> result = new ArrayList<>();
        for (int i = 0; i < num; i++) {
            result.add(MathUtil.getRandomInt(0, 100));
        }
        return result;
    }

    /**
     * 创建Dish List
     *
     * @param num 个数
     * @return List<Dish>
     */
    List<Dish> createDishes(int num) {
        List<Dish> result = new ArrayList<>();

        List<String> names = Stream.of("pork", "beef", "chicken", "french", "rice").collect(Collectors.toList());
        List<Boolean> isVergetable = Stream.of(true, false).collect(Collectors.toList());
        List<Dish.Type> types = Stream.of(Dish.Type.MEAT, Dish.Type.FISH, Dish.Type.OTHER).collect(Collectors.toList());

        for (int i = 0; i < num; i++) {
            String name = RandomUtil.getRandomItem(names);
            boolean isVergetab = RandomUtil.getRandomItem(isVergetable);
            int calory = MathUtil.getRandomInt(200, 1000);
            Dish.Type type = RandomUtil.getRandomItem(types);


            Dish dish = new Dish(name, isVergetab, calory, type);
            result.add(dish);
        }

        return result;
    }

}
